#include <qthread.h>
#include <qdebug.h>
#include <qprocess.h>

#include <SimulateThread.h>

SimulateThread::SimulateThread(QObject * parent) : QThread(parent)
{
	memset(board, 0, sizeof(board));
}

SimulateThread::~SimulateThread()
{
	if (e1)
	{
		e1->kill();
		delete e1;
	}

	if (e2)
	{
		e2->kill();
		delete e2;
	}
}

void SimulateThread::run()
{
	for (int i = 0; i < times; i++)
	{
		memset(board, 0, sizeof(board));
		e1 = new QProcess();
		e2 = new QProcess();
		connect(e1, &QProcess::readyReadStandardOutput, this, &SimulateThread::receiveEngine1Data);
		connect(e2, &QProcess::readyReadStandardOutput, this, &SimulateThread::receiveEngine2Data);

		if (mode == 2)
		{
			nowFirst = first;
			turn = first;
		}
		else
		{
			nowFirst = 1;
			turn = 1;
		}

		e1->start(engine1);
		e2->start(engine2);
		writeProcess(e1, "11\r\n");
		e1->waitForReadyRead();

		writeProcess(e1, QString("%1\r\n").arg(nowFirst));
		if (nowFirst == 1)
		{
			e1->waitForReadyRead();
		}

		writeProcess(e2, "11\r\n");
		e2->waitForReadyRead();

		writeProcess(e2, QString("%1\r\n").arg(3 - nowFirst));
		if (nowFirst == 2)
		{
			e2->waitForReadyRead();
		}

		while (1)
		{
			e1->waitForReadyRead(500);
			e2->waitForReadyRead(500);
			while (!cmdQueue.empty())
			{
				queueMutex.lock();
				QPair<QString, int> cmd = cmdQueue.front();
				cmdQueue.pop_front();
				queueMutex.unlock();

				QString str = cmd.first;
				int carrier = cmd.second;
				//QStringList list = str.split("\r\n");
				qDebug() << str << carrier;
				if (str.indexOf("MODE") != -1)
				{
					/*if (carrier == 1)
					{
						writeProcess(e1, "11\r\n");
						if (nowFirst == 1)
						{
							e1->waitForReadyRead();
						}
					}
					else if (carrier == 2)
					{
						writeProcess(e2, "11\r\n");
						if (nowFirst == 2)
						{
							e2->waitForReadyRead();
						}
					}*/
				}
				else if (str.indexOf("TIME") != -1)
				{
					if (carrier == 1)
					{
						writeProcess(e1, "50\r\n");
						e1->waitForReadyRead();
					}
					else
					{
						writeProcess(e2, "50\r\n");
						e2->waitForReadyRead();
					}
				}
				else if (str.indexOf("MOVE") != -1)
				{
					if (carrier == 1)
					{
						QPair<QString, int> move = cmdQueue.front();
						cmdQueue.pop_front();
						lastMove = move.first.toInt();
						if (board[lastMove / 11][lastMove % 11] != 0)
						{
							throw std::exception();
						}
						board[lastMove / 11][lastMove % 11] = 1;
						emit makeMove(lastMove, 1);
					}
					else if (carrier == 2)
					{
						QPair<QString, int> move = cmdQueue.front();
						cmdQueue.pop_front();
						lastMove = move.first.toInt();
						if (board[lastMove / 11][lastMove % 11] != 0)
						{
							throw std::exception();
						}
						board[lastMove / 11][lastMove % 11] = 2;
						emit makeMove(lastMove, 2);
					}
				}
				else if (str.indexOf("OPPONENT") != -1)
				{
					if (carrier == 1)
					{
						writeProcess(e2, QString("%1\r\n").arg(lastMove));
						//e2->waitForReadyRead();
					}
					else if (carrier == 2)
					{
						writeProcess(e1, QString("%1\r\n").arg(lastMove));
						//e1->waitForReadyRead();
					}
				}
			}

			int over = isGameOver();
			if (over)
			{
				qDebug() << "win: " << over;
				break;
			}
		}

		e1->kill();
		e2->kill();
		delete e1;
		delete e2;
		e1 = nullptr;
		e2 = nullptr;
	}
}

void SimulateThread::setParam(QString & engine1, QString & engine2, int times, int mode, int first)
{
	this->engine1 = engine1;
	this->engine2 = engine2;
	this->times = times;
	this->mode = mode;
	this->first = first;
}

void SimulateThread::writeProcess(QProcess * process, const QString & data)
{
	process->write(data.toLatin1().constData(), data.length());
	process->waitForBytesWritten();
}

void SimulateThread::receiveEngine1Data()
{
	//QString data = " ";

	////while (data != "")
	////{
	//	data = e1->readAll();
	//	qDebug() << "engine1: " << data;
	//	if (data == "MODE\r\n")
	//	{
	//		//writeProcess(e1, QString("%1\r\n").arg(nowFirst));
	//		//e1->waitForReadyRead();
	//		cmdStack.push(QPair<QString, int>("MODE", 1));
	//	}
	//	else if (data == "TIME\r\n")
	//	{
	//		//writeProcess(e1, "5000\r\n");
	//		//e1->waitForReadyRead();
	//		cmdStack.push(QPair<QString, int>("TIME", 1));
	//	}
	//	else if (data == "MOVE\r\n")
	//	{
	//		QString move = e1->readLine();
	//		lastMove = move.toInt();
	//		emit makeMove(lastMove, 1);
	//		qDebug() << "e1 Move: " << lastMove;
	//	}
	//	else if (data == "OPPONENT\r\n")
	//	{
	//		turnFlag = true;
	//		writeProcess(e2, QString("%1\r\n").arg(lastMove));
	//		//e2->waitForReadyRead();
	//	}
	////}
	//
	QString data;
	if (!queueMutex.tryLock())
	{
		return;
	}
	//queueMutex.lock();
	do
	{
		data = e1->readLine();
		if (data.indexOf("MOVE") != -1)
		{
			e1->waitForReadyRead();
		}
		cmdQueue.push_back(QPair<QString, int>(data, 1));
	} while (data != "");
	queueMutex.unlock();
	//cmdQueue.push_back(QPair<QString, int>(e1->readAll(), 1));
}

void SimulateThread::receiveEngine2Data()
{
	//QString data = " ";

	////while (data != "")
	////{
	//	data = e2->readLine();

	//	qDebug() << "engine2:" << data;
	//	if (data == "MODE\r\n")
	//	{
	//		//writeProcess(e2, QString("%1\r\n").arg(3 - nowFirst));
	//		//e2->waitForReadyRead();
	//		cmd = "MODE";
	//		carrier = 2;
	//	}
	//	else if (data == "TIME\r\n")
	//	{
	//		writeProcess(e2, "5000\r\n");
	//		//e2->waitForReadyRead();
	//		cmd = "TIME";
	//		carrier = 2;
	//	}
	//	else if (data == "MOVE\r\n")
	//	{
	//		QString move = e2->readLine();
	//		lastMove = move.toInt();
	//		emit makeMove(lastMove, 2);
	//		qDebug() << "e2 Move: " << lastMove;
	//	}
	//	else if (data == "OPPONENT\r\n")
	//	{
	//		turnFlag = true;
	//		writeProcess(e1, QString("%1\r\n").arg(lastMove));
	//		//e1->waitForReadyRead();
	//	}
	////}
	//queueMutex.lock();
	if (!queueMutex.tryLock())
	{
		return;
	}
	QString data;
	do
	{
		data = e2->readLine();
		if (data.indexOf("MOVE") != -1)
		{
			e2->waitForReadyRead();
		}
		cmdQueue.push_back(QPair<QString, int>(data, 2));
	} while (data != "");
	queueMutex.unlock();
	//cmdQueue.push_back(QPair<QString, int>(e2->readAll(), 2));
}

int SimulateThread::isGameOver()
{
	int vis[11][11] = { 0 };
	QStack<int> st;
	for (int i = 0; i < 11; i++)
	{
		if (board[0][i] == 2)
		{
			st.push(i);
			vis[0][i] = 1;
		}
	}
	while (!st.empty())
	{
		int p = st.top();
		st.pop();
		int px = p / 11;
		int py = p % 11;
		for (int i = 0; i < 6; i++)
		{
			int nx = px + d[i][0];
			int ny = py + d[i][1];
			int npos = nx * 11 + ny;
			if (nx >= 0 && nx < 11 && ny >= 0 && ny < 11 && !vis[nx][ny] && board[nx][ny] == 2)
			{
				if (nx == 10)
				{
					return 2;
				}
				vis[nx][ny] = true;
				st.push(npos);
			}
		}
	}

	memset(vis, 0, sizeof(vis));
	while (!st.empty())
	{
		st.pop();
	}
	for (int i = 0; i < 11; i++)
	{
		if (board[i * 11][0] == 1)
		{
			st.push(i * 11);
			vis[i][0] = 1;
		}
	}
	while (!st.empty())
	{
		int p = st.top();
		st.pop();
		int px = p / 11;
		int py = p % 11;
		for (int i = 0; i < 6; i++)
		{
			int nx = px + d[i][0];
			int ny = py + d[i][1];
			int npos = nx * 11 + ny;
			if (nx >= 0 && nx < 11 && ny >= 0 && ny < 11 && !vis[nx][ny] && board[nx][ny] == 1)
			{
				if (ny == 10)
				{
					return 1;
				}
				vis[nx][ny] = 1;
				st.push(npos);
			}
		}
	}
	return 0;
}